package main

import (
	"log"     // 用于日志输出
	"net/rpc" // 用于实现RPC客户端
)

// 定义与服务端一致的请求和响应数据结构
// 注意：客户端与服务端的结构体字段类型、名称必须完全一致（序列化/反序列化需要）

// GetUserReq 发送给服务端的查询请求结构
type GetUserReq struct {
	Id string `json:"id"` // 要查询的用户ID，json标签用于序列化（与服务端对应）
}

// GetUserResp 接收服务端返回的查询响应结构
type GetUserResp struct {
	Id    string // 用户ID
	Name  string // 用户名
	Phone string // 电话号码
}

func main() {
	// 1. 建立与RPC服务端的TCP连接
	// 第一个参数：网络类型（此处为tcp）
	// 第二个参数：服务端地址（localhost:1234，与服务端监听的地址一致）
	// rpc.Dial会内部创建连接并初始化默认的gob编解码器
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		// 连接失败则致命错误（如服务端未启动、端口错误、网络不通等）
		log.Fatal("无法与服务端建立连接：", err)
	}
	// 延迟关闭连接：确保程序退出时释放网络资源
	defer func(client *rpc.Client) {
		err := client.Close()
		if err != nil {
			log.Println("关闭连接失败：", err)
		}
	}(client)

	// 2. 准备请求参数和响应接收变量
	// 构造查询请求：查询ID为"1"的用户
	req := GetUserReq{Id: "1"}
	// 定义响应变量：必须是指针类型（RPC框架需要通过指针修改其值）
	var resp GetUserResp

	// 3. 同步调用远程方法
	// client.Call参数说明：
	// 第一个参数：服务方法名（格式为"服务名.方法名"，需与服务端注册的一致）
	// 第二个参数：请求参数（需与服务端方法的第一个参数类型一致）
	// 第三个参数：响应结果指针（需与服务端方法的第二个参数类型一致）
	// Call方法是同步的，会阻塞当前goroutine直到收到服务端响应
	err = client.Call("UserServer.GetUser", req, &resp)
	if err != nil {
		// 调用失败：可能是网络错误（如连接断开）或业务错误（如"没有找到用户"）
		// 可通过类型断言区分错误类型（参考之前的场景题）
		log.Println("远程调用失败：", err)
		return
	}

	// 4. 处理成功响应
	// 打印服务端返回的用户信息
	log.Printf("查询成功，用户信息：%+v", resp)
}
